<template>
  <div class="goods">
    <div class="wrap">
      <Crumb> </Crumb>
      <img src="../assets/images/banner.4c6b6225.png" width="100%" alt="" />
      <ul class="options">
        <li>
          <strong>排序：</strong>
          <span
            v-for="(item, index) in optionsByOrderArr"
            :key="index"
            :class="{ active: currentOption == index }"
            @click="optionsClcikByOrder(index, item.min, item.max)"
            >{{ item.txt }}</span
          >
        </li>
        <li>
          <strong>分类：</strong>
          <span
            v-for="(item, index) in optionsByGroupArr"
            :key="index"
            :class="{ active: currentGroup == index }"
            @click="optinsClickByGroup(index, item.type)"
            >{{ item.txt }}</span
          >
        </li>
      </ul>
      <!-- <List :productdata="productsArr" /> -->
      <List :productdata="productsArrShow" />
      <div>{{ isReachBottom ? "加载完毕" : "正在加载中..." }}</div>
    </div>
  </div>
</template>                            
<script>
import Crumb from "../components/Crumb.vue";
import List from "../components/home/List.vue";
import { ProductsAPI } from "../request/api";
import { mapState } from "vuex";
import { getScrollTop, getClientHeight, getScrollHeight } from "../utils";

let optionsByOrderArr = [
  { min: 0, max: 0, txt: "全部" },
  { min: 0, max: 0, txt: "我还可以兑换" },
  { min: 0, max: 500, txt: "0-500个" },
  { min: 500, max: 1000, txt: "5-1000个" },
  { min: 1000, max: 1500, txt: "1000-1500个" },
];
let optionsByGroupArr = [
  { type: 0, txt: "全部" },
  { type: 1, txt: "实物礼品" },
  { type: 2, txt: "虚拟礼品" },
];

export default {
  data() {
    return {
      optionsByOrderArr, //排序条件数组
      currentOption: 0, //当前排序条件索引
      optionsByGroupArr, //分类条件数组
      currentGroup: 0, //当前分类条件索引

      productsArrShow: [], //滚动加载的数据
      productsArr: [], //商品数据
      did: 0, //栏目 1->精品推荐 2-> 热门兑换 0->全部
      min: 0, //大于多少积分，最少是0
      max: 0, //少于多少积分，最多是10000，当传入0的时候，会直接返回所有商品，无视后台逻辑。
      type: 0, //1->实体商品 2->虚拟商品 0->全部
      keyword: "", //商品关键词

      //页码
      page: 1,
      //大小 每页显示多少条
      size: 8,
      isLoading: false, //是否正在加载
      isReachBottom: false, //是否加载完毕
    };
  },
  computed: {
    ...mapState({
      mycoin: (state) => state.userInfo.userInfo.coin,
    }),
  },
  components: {
    Crumb,
    List,
  },
  created() {
    //更多
    let did = this.$route.query.did;
    if (did) {
      this.did = did;
    }

    //如果有keyword,则是搜索
    let keyword = this.$route.query.keyword;
    if (keyword) {
      this.keyword = keyword;
    }
    this.requestGoods();
  },
  mounted() {
    //当组件挂载完成,才绑定滚动事件，注意:调用函数如果加() 是立刻执行,不加()是事件触发后执行
    window.addEventListener("scroll", this.scrollFn);
  },
  beforeDestroy() {
    //当组件销毁就解绑事件，否则在其他组件也会触发该滚动事件
    window.removeEventListener("scroll", this.scrollFn);
  },
  // 当路由路径不变,参数变化触发两种方式
  // 方式一：监听
  // watch:{
  //   //如果keyword有变化,则再次获取并发送请求
  //   "$route.query.keyword":{
  //     handler(newVal,oldVal){
  //       // 重新获取新的值
  //       this.keyword = newVal;
  //       // 再次发送请求
  //       this.requestGoods();
  //     }
  //   }
  // },

  // 方式二:路由的钩子函数
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变，但是该组件被复用时调用
    // 举例来说，对于一个带有动态参数的路径 /foo/:id，在 /foo/1 和 /foo/2 之间跳转的时候，
    // 由于会渲染同样的 Foo 组件，因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`

    // console.log("beforeRouteUpdate",this,to.query,from.query);

    //什么时候执行?当keyword参数变化时,触发
    next();
    console.log("beforeRouteUpdate", to.query.keyword, from.query.keyword);
    // 重新获取新的值
    this.keyword = to.query.keyword;
    // 再次发送请求
    this.requestGoods();
  },
  methods: {
    optionsClcikByOrder(index, min, max) {
      //我还可以兑换,此时max的值是我的积分
      this.max = index == 1 ? this.mycoin : max;
      this.currentOption = index;
      this.min = min;
      this.requestGoods();
    },
    optinsClickByGroup(index, type) {
      this.currentGroup = index;
      this.type = type;
      this.requestGoods();
    },
    // 请求商品数据
    async requestGoods() {
      let productRes = await ProductsAPI({
        did: this.did,
        min: this.min,
        max: this.max,
        type: this.type,
        keyword: this.keyword,
      });
      if (!productRes) {
        return;
      }
      if (productRes.code == 0) {
        this.productsArr = productRes.data;
        //初始化:显示第一页数据 8 条
        this.productsArrShow = productRes.data.filter(
          (item, index) => index < 8
        );
      }

      //初始化:默认加载第1页
      //bug1:当全部加载完毕,再点击其他,此时显示正在加载中,但是始终没有请求到数据,原因:page页数一直在增加超出页面范围了
      //解决:每次发送请求：初始化page
      this.page = 1;

      //初始化: 判断是否加载完毕
      //bug2:当正在加载中,此时切换分类,如:切换虚拟礼品,还显示正在加载中
      //解决:当筛选数据,进行初始化后判断是否加载完毕
      //先初始化
      this.isReachBottom = false;
      //判断数据是否加载完毕
      if (this.productsArrShow.length == this.productsArr.length) {
        this.isReachBottom = true;
        return;
      }
    },
    scrollFn() {
      /*
      页码概念？滚动到一定位置时，加载下一页的数据。
      打开产品页面时，默认只加载第一页的数据（8条）
      正常情况：每次加载下一页数据时，是往后端发起新的请求请求某一页数据。
      积分商城：由于后端已经返回了所有数据goodslist给我们，我们今天是做一个模拟。
      滚动到一定位置时，往goodslist获取下一页的数据。

      1.默认只加载第一页的数据(8条)
      2.组件挂载完成mounted()绑定滚动事件...onscrol1
      3.组件销毁前beforeDestroy()解绑该滚动事件
      4.判断滚动是否已到达商品底部,到了准备加载下一页数据
      5.加载下一页
        目标：从productsArr获取数据然后追加productsArrShow
        page:1 size:8 对应数组下标:0~7
        page:2 size:8 对应数组下标:8~15
        page:3 size:8 对应数组下标:16~23
        page:4 size:8 对应数组下标:24~31
        分页规律：(page-1)*size ~ page*size-1
        page:当前页
        size:每页多少条数据
      */

      //滚动事件
      // console.log("滚动了",getScrollTop(),getClientHeight(),getScrollHeight());
      // 判断是否滚动到页面底部: 可视化高度+滚动高度 = 文档高度
      //判断是否滚动到该商品底部位置: 可视化高度+滚动高度 >= 文档高度-底部高度    即 可加载下页的数据
      if (getScrollTop() + getClientHeight() >= getScrollHeight() - 240) {
        //判断数据是否加载完毕
        if (this.productsArrShow.length == this.productsArr.length) {
          this.isReachBottom = true;
          return;
        }

        // console.log("滚动了");
        //加载下一页数据,然后追加
        // this.page++;
        //加载下一页数据
        // 方式1
        // let nextArr = this.productsArr.filter((item, index) => {
        //   return index >= (this.page - 1)*this.size && index<=(this.page*this.size)-1;
        // });
        // 追加
        // this.productsArrShow.push(...nextArr);

        //方式2 :普通for循环
        //是往后端发送新的请求(以后项目这里可能是一个请求,2s) --此处用setTimeOut()模拟
        // setTimeout(() => {
        //   for (
        //     let i = (this.page - 1) * this.size;
        //     i <= this.page * this.size - 1;
        //     i++
        //   ) {
        //     console.log("响应数据:","page:",this.page,"i:", i);
        //     // 此时会报id错误,因为当数据只有28条,取不到那个索引值,相当于渲染时 undefined.id
        //     // 当有值的时候,才push
        //     this.productsArr[i] &&
        //       this.productsArrShow.push(this.productsArr[i]);
        //   }
        // }, 2000);

        //此时会有一个bug,当我们快速滚动,会频繁的发送请求,此时请求是异步请求,数据还没响应,又再次发送请求.
        //会导致请求频繁发送以及数据响应未及时,页面未渲染完就发送请求,最后一次性响应很多请求
        //需求:加载完当前这一页时，再去加载下一页的数据
        //解决方案：节流-> 让一段代码在一段时间之内只执行一次
        //设置一个开关,当没有请求则发送请求,当正在发送请求,此时关闭开关,请求完成响应后,开启开关,下个请求才能发送

        //解决如下:
        //当没有加载时
        if (this.isLoading == false) {
          this.page++;
          //开始加载下一页数据
          this.isLoading = true;
          setTimeout(() => {
            for (
              let i = (this.page - 1) * this.size;
              i <= this.page * this.size - 1;
              i++
            ) {
              console.log("响应数据:", "page:", this.page, "i:", i);
              // 此时会报id错误,因为当数据只有28条,取不到那个索引值,相当于渲染时 undefined.id
              // 当有值的时候,才push
              this.productsArr[i] &&
                this.productsArrShow.push(this.productsArr[i]);
            }
            // 加载完毕了
            this.isLoading = false;
          }, 2000);
        }
      }
    },
  },
};
</script>
  
<style lang = "less" scoped>
@import "../assets/css/public.less";

.goods {
  padding-bottom: 50px;
  background: #efefef;
  padding-top: 20px;
  .options {
    padding-top: 20px;
    padding-bottom: 40px;
    li {
      margin-top: 20px;
      strong {
        color: #000;
        font-weight: bold;
      }
      span {
        margin-right: 20px;
        margin-left: 10px;
        cursor: pointer;
        color: #999;
        &.active {
          color: @base-color;
          font-weight: bold;
        }
      }
    }
  }
}
</style>